package tinyiotserver

import (
	"log"
	"sync"
	"time"

	"gitee.com/mqyqingkong/tiny-iot-server/common"
)

const (
	//请求授权
	REQ_AUTH string = "REQ_AUTH"
	//响应授权
	RES_AUTH string = "RES_AUTH"
)

// NewSimpleAuthMsgHandler 授权设备接入。
// 创建设备接入授权Handler(简单版本，可仿造该版本随意定制)。
// authorizeLogic：决定是否授权。
// authCodeGen：生成授权码。
func NewSimpleAuthMsgHandler(authorizeLogic func(msg *Message) bool, authCodeGen func(msg *Message) string) MessageHandler {
	h := &simpleAuthMsgHandler{
		authCodes:          map[uint32]string{},
		currentMaxClientId: 0,
		mux:                sync.RWMutex{},
		authorize:          authorizeLogic,
		authCodeGen:        authCodeGen,
	}
	if authorizeLogic == nil {
		h.authorize = func(msg *Message) bool {
			return false
		}
	}
	if authCodeGen == nil {
		h.authCodeGen = func(msg *Message) string {
			return time.Now().Format(time.RFC3339Nano)
		}
	}
	return h
}

// simpleAuthMsgHandler 设备接入授权
type simpleAuthMsgHandler struct {
	MsgHandlerAdapter
	//clientId : authCode
	authCodes          map[uint32]string
	currentMaxClientId uint32
	mux                sync.RWMutex
	//决定是否授权
	authorize func(msg *Message) bool
	//生成授权码
	authCodeGen func(msg *Message) string
}

func (h *simpleAuthMsgHandler) OnReceive(requestMsg *Message) (newMsg *Message) {
	tag := "simpleAuthMsgHandler"
	topic := string(requestMsg.Body.Topic)
	if requestMsg.Header.Type == common.POST_WAIT_RESPONSE && topic == REQ_AUTH {
		//请求授权
		msgCp := requestMsg.Copy()
		if !h.authorize(msgCp) {
			log.Printf("[%s] auth fail, clientId[%d] can not be authorized", tag, requestMsg.Header.ClientId)
			return FailMsg(topic, "auth invalid")
		}
		authCode := h.authCodeGen(msgCp)
		h.mux.Lock()
		h.currentMaxClientId++
		h.authCodes[h.currentMaxClientId] = authCode
		h.mux.Unlock()

		respMsg := &Message{
			Msg: common.NewMsg(common.MsgHeader{
				Version:  common.Version,
				Type:     common.RESPONSE,
				ClientId: h.currentMaxClientId,
			},
				common.MsgBody{
					Topic:   []byte(RES_AUTH),
					Payload: []byte(authCode),
				}),
		}

		return respMsg
	} else {
		//验证授权
		clientId := requestMsg.Header.ClientId

		h.mux.RLock()
		authCodeShould, found := h.authCodes[clientId]
		h.mux.RUnlock()
		if !found {
			log.Printf("[%s] auth invalid, clientId[%d] not authorized", tag, clientId)
			return FailMsg(topic, "auth invalid")
		}

		payload := requestMsg.Body.Payload
		if len(payload) < len(authCodeShould) {
			log.Printf("[%s] auth invalid, clientId:%d, payload:%s", tag, clientId, string(payload))
			return FailMsg(topic, "auth invalid")
		}

		clientCode := string(payload[:len(authCodeShould)])
		//验证授权码
		if authCodeShould != clientCode {
			log.Printf("[%s] auth invalid, clientId:%d, clientCode:%s", tag, clientId, clientCode)
			return FailMsg(topic, "auth invalid")
		}
		//重新设置消息Payload
		if len(payload) > len(authCodeShould) {
			requestMsg.SetPayload(payload[len(authCodeShould):])
		}
		//验证通过
		return
	}
}